package com.csun.cmny.provider.web.websocket;

import com.csun.cmny.provider.consumer.message.MessageSender;
import com.csun.cmny.provider.model.dto.SendMessageRequest;
import com.csun.cmny.provider.model.dto.SendWebSocketRequest;
import com.csun.cmny.provider.model.vo.OpcUserVo;
import com.csun.cmny.provider.model.vo.RpcRoleVo;
import com.csun.cmny.provider.model.vo.nursing.NursingGroupUserVo;
import com.csun.cmny.provider.service.OmcRpcService;
import com.csun.cmny.provider.service.OpcOssService;
import com.csun.cmny.provider.service.UacRpcService;
import com.csun.cmny.util.ApplicationContextUtil;
import com.csun.cmny.util.PublicUtil;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import net.sf.json.JSONObject;
import org.springframework.beans.BeanUtils;
import org.springframework.context.annotation.DependsOn;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArraySet;

/**
 * @Auther: chisj chisj@foxmal.com
 * @Date: 2019-03-20 20:30
 * @Description: the class web socket server
 *
 * ws://192.168.1.111:7050/websocket/{userId}
 *
 * ws://192.168.1.111:7050/websocket/272
 *
 */
@Data
@Slf4j
@Service("WEBSOCKET")
@DependsOn("applicationContextUtil")
@ServerEndpoint("/websocket/{userId}")
public class WebSocketServer implements MessageSender {

//    @Resource
//    private UacRpcService uacRpcService;
//    @Resource
//    private OmcRpcService omcRpcService;

    static UacRpcService uacRpcService = (UacRpcService) ApplicationContextUtil.getBeanByClass(UacRpcService.class);
    static OmcRpcService omcRpcService = (OmcRpcService) ApplicationContextUtil.getBeanByClass(OmcRpcService.class);

    // 静态变量，用来记录当前在线连接数。应该把它设计成线程安全的。
    private static int onlineCount = 0;

    //concurrent包的线程安全Set，用来存放每个客户端对应的MyWebSocket对象。
    private static CopyOnWriteArraySet<WebSocketServer> webSocketSet = new CopyOnWriteArraySet<WebSocketServer>();

    private static ConcurrentHashMap<Long, WebSocketServer> webSocketMap = new ConcurrentHashMap<>();

    //与某个客户端的连接会话，需要通过它来给客户端发送数据
    // private Session session;
    /**
     * 一个用户可能在多处登录
     */
    private List<Session> sessions = Lists.newArrayList();

    /**
     * 机构Id
     */
    private Long groupId;
    /**
     * 机构名称
     */
    private String groupName;
    /**
     * 用户Id
     */
    private Long userId;
    /**
     * 用户姓名
     */
    private String userName;
    /**
     * 用户登录名
     */
    private String loginName;
    /**
     * 班组Id
     */
    private Long nursingGroupId;
    /**
     * 角色集合
     */
    private List<RpcRoleVo> roleList;

    /**
     * 连接建立成功调用的方法
     */
    @OnOpen
    public void onOpen(Session session, @PathParam("userId") Long userId) {

        // this.session = session;
        this.userId = userId;
        WebSocketServer server = webSocketMap.get(userId);
        if (PublicUtil.isEmpty(server)) {
            server = this;
        }

        server.sessions.add(session);

        // 1、根据用户Id获取用户信息
        OpcUserVo opcUserVo = uacRpcService.queryOpcUserVoByUserId(userId);
        log.info("opc user vo ==> {}", opcUserVo.toString());
        if (PublicUtil.isNotEmpty(opcUserVo)) {
            BeanUtils.copyProperties(opcUserVo, server);
        } else {
            log.error("websocket 连接非法");
            throw new RuntimeException("websocket 连接非法");
        }

        // 2、根据用户Id获取所在班组
//        NursingGroupUserVo nursingGroupUserVo = omcRpcService.queryNursingGroupUserByUserId(userId);
//        if (PublicUtil.isNotEmpty(nursingGroupUserVo)
//                && PublicUtil.isNotEmpty(nursingGroupUserVo.getNursingGroupId())) {
//            this.nursingGroupId = nursingGroupUserVo.getNursingGroupId();
//        }

        try {
            // sendMessage("连接成功");
        } catch (Exception e) {
            log.error("websocket IO异常");
        }

        webSocketMap.put(userId, server);
        addOnlineCount();           //在线数加1
        log.info("有新窗口开始监听 session id = {}, userId = {}, userName = {}, groupId= {}, nursingGroupId = {}, roleList = {}, 当前在线人数 = {}",
                session.getId(), userId, server.userName, server.groupId, server.nursingGroupId, server.roleList, getOnlineCount());
    }

    /**
     * 连接关闭调用的方法
     */
    @OnClose
    public void onClose(Session session, @PathParam("userId") Long userId) {
        // webSocketSet.remove(this);  //从set中删除

        if (PublicUtil.isEmpty(userId)) {
            log.error("=====> chisj user id is null.");
            return ;
        }

        // 移除session
        WebSocketServer server = webSocketMap.get(userId);
        if (PublicUtil.isEmpty(server)) {
            log.error("=====> chisj server is null.");
            return ;
        }

        log.info("移除session id = {}, userId = {} userName = {}, roleList = {}, groupId = {}, nursingGroupId = {}",
                session.getId(), userId, server.userName, server.roleList, server.groupId, server.nursingGroupId);

        if (PublicUtil.isEmpty(server.sessions)) {
            webSocketMap.remove(userId);
        } else {
            server.sessions.remove(session);
        }

        subOnlineCount();           //在线数减1
        log.info("有一连接关闭！当前在线人数为" + getOnlineCount());
    }

    /**
     * 收到客户端消息后调用的方法
     *
     * @param message 客户端发送过来的消息
     */
    @OnMessage
    public void onMessage(String message, Session session) {

        log.info("收到来自窗口" + userId + "的信息:" + message);

        session.getAsyncRemote().sendText(message);
        //群发消息
//        for (WebSocketServer item : webSocketSet) {
//            try {
//                item.sendMessage(message);
//            } catch (Exception e) {
//                e.printStackTrace();
//            }
//        }
    }

    /**
     * @param session
     * @param error
     */
    @OnError
    public void onError(Session session, Throwable error) {
        log.error("发生错误");
        error.printStackTrace();
    }

    /**
     * 实现服务器主动推送
     */
    public void sendMessage(String message) {

        try {
            // this.session.getBasicRemote().sendText(message);
            for (Session s : sessions) {
                s.getBasicRemote().sendText(message);
                log.info("websocket 推送 ===>session id = {},  userId = {}, 姓名 = {}， message = {}",
                        s.getId(), this.userId, this.userName, message);
            }
        } catch (Exception e) {
            log.error("websocket 推送 ===> 错误信息 = {}", e.getMessage());
        }

    }

    /**
     * @param sendMessageRequest
     */
    @Override
    public void sendMessage(SendMessageRequest sendMessageRequest) {

        log.info("websocket send message 推送 ===> sendMessageRequest = {}", sendMessageRequest);

        // 封装消息
        Map<String, Object> map = Maps.newHashMap();
        map.put("msgType", sendMessageRequest.getMsgCode());
        map.put("show", sendMessageRequest.getShow());
        map.put("title", sendMessageRequest.getMessageTitle());
        map.put("name", sendMessageRequest.getMessageName());
        map.put("url", sendMessageRequest.getMessageUrl());
        map.put("content", sendMessageRequest.getMessage());

        // String message = map.toString();
        String message = JSONObject.fromObject(map).toString();
        //
        if (PublicUtil.isNotEmpty(sendMessageRequest.getUserIdList())) {  // 推送用户列表
            sendMessageWithUserId(sendMessageRequest.getUserIdList(),
                    message);
        } else {
            if (PublicUtil.isNotEmpty(sendMessageRequest.getRoleCodeList())) {    // 推送指定机构的角色列表
                sendMessageWithRoleCode(sendMessageRequest.getRoleCodeList(),
                        sendMessageRequest.getGroupId(),
                        message);
            } else {    // 推送指定机构
                sendMessageWithGroupId(sendMessageRequest.getGroupId(),
                        message);
            }
        }
    }

    public void sendMessageDefault(SendWebSocketRequest sendWebSocketRequest) {

        log.info("websocket default 推送 ===> sendWebSocketRequest = {}", sendWebSocketRequest);

        log.error("接口作废......");
//        if (PublicUtil.isNotEmpty(sendWebSocketRequest.getUserIdList())) {  // 推送用户列表
//            sendMessageWithUserId(sendWebSocketRequest.getUserIdList(),
//                    sendWebSocketRequest.getMessage());
//        } else {
//            if (PublicUtil.isNotEmpty(sendWebSocketRequest.getRoleCodeList())) {    // 推送指定机构的角色列表
//                sendMessageWithRoleCode(sendWebSocketRequest.getRoleCodeList(),
//                        sendWebSocketRequest.getGroupId(),
//                        sendWebSocketRequest.getMessage());
//            } else {    // 推送指定机构
//                sendMessageWithGroupId(sendWebSocketRequest.getGroupId(),
//                        sendWebSocketRequest.getMessage());
//            }
//        }

    }

    public void sendMessageWithUserId(List<Long> userIdList, String message) {

        log.info("websocket 推送 ===> userIdList = {}， message = {}", userIdList, message);

        for (Long userId : userIdList) {
            WebSocketServer server = webSocketMap.get(userId);
            if (PublicUtil.isNotEmpty(server)) {
                server.sendMessage(message);
            } else {
                // 用户不在线
                log.warn("websocket 推送 ===>  [{}] 离线了...", userId);
            }
        }
    }

    public void sendMessageWithGroupId(Long groupId, String message) {

        log.info("websocket 推送 ===> groupId = {}， message = {}", groupId, message);

        // 遍历webSocketMap，推送groupId所有的用户
        for(Map.Entry<Long, WebSocketServer> entry : webSocketMap.entrySet()){
            Long userId = entry.getKey();
            WebSocketServer server = entry.getValue();
            if (server.getGroupId().equals(groupId)) {
                server.sendMessage(message);
            }
        }
    }

    public void sendMessageWithRoleCode(List<String> roleCodeList, Long groupId, String message) {
        log.info("websocket 推送 ===> roleCodeList = {}， groupId = {}, message = {}",
                roleCodeList, groupId, message);

        // 遍历webSocketMap，推送groupId所有的用户
        for(Map.Entry<Long, WebSocketServer> entry : webSocketMap.entrySet()){
            Long userId = entry.getKey();
            WebSocketServer server = entry.getValue();
            for (RpcRoleVo r : server.getRoleList()) {

//                log.info("group id => {}, groupId => {}", server.getGroupId(), groupId);
//                if (server.getGroupId().equals(groupId)) {
//                    log.info("group equals true");
//                } else {
//                    log.info("group equals false");
//                }
//
//                for (String c : roleCodeList) {
//                    log.info("role code  => {}, roleCode => {}", c, r.getRoleCode());
//                    if (c.equals(r.getRoleCode())) {
//                        log.info("role code equals true");
//                    } else {
//                        log.info("role code equals false");
//                    }
//                }

                if (server.getGroupId().equals(groupId)   // 指定机构
                        && roleCodeList.contains(r.getRoleCode())) { // 不管是否有多个角色符合，该用户只推送一次
                    server.sendMessage(message);
                    break;
                }
            }
        }
    }

    /**
     * 群发自定义消息
     */
//    public static void sendMessage(String message, @PathParam("groupId") String groupId) throws IOException {
//        log.info("推送消息到窗口" + groupId + "，推送内容:" + message);
//        for (WebSocketServer item : webSocketSet) {
//            try {
//                //这里可以设定只推送给这个sid的，为null则全部推送
//                if (groupId == null) {
//                    item.sendMessage(message);
//                } else if (item.groupId.equals(groupId)) {
//                    item.sendMessage(message);
//                }
//            } catch (IOException e) {
//                continue;
//            }
//        }
//    }

    public static synchronized int getOnlineCount() {
        return onlineCount;
    }

    public static synchronized void addOnlineCount() {
        WebSocketServer.onlineCount++;
    }

    public static synchronized void subOnlineCount() {
        WebSocketServer.onlineCount--;
    }


}
